package com.MHSoftware.net.IP;
/*
   MHS.jar Class Library
   Version 1.5.
   Copyright (C) MH Software, Inc. 1999-2000
   George Sexton gsexton@mhsoftware.com

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/***	Simple Class to do some IP Address manipulation, including
 *	applying a subnet mask to an address.
 **	Sample Code
 **	<PRE>
 *	import com.MHSoftware.net.IP.*;
 *	...
 *	boolean bResult=false;
 *	try {
 *		ip=new IpAddress(cUserIPAddress);
 *		ip.applyNetMask(oUser.NetMask);
 *		bResult=ip.equals(oUser.AllowedNetwork);
 *	} catch (MalformedIpAddressException mfe) {
 *	}
 **	</PRE>
 **	@author gsexton
 */
public class IpAddress {

    private static final int MAX_IP_OCTETS=32;
    
    private short[] aOctets=new short[4];
    
    /**	Construct a new blank IP Address (0.0.0.0)
     */
    public IpAddress() {
    }
    
    /**	Construct a new IP Address object from an address string. I.E. 208.139.197.195
     * @param Octets
     */
    public IpAddress(String Octets) throws MalformedIpAddressException {
	aOctets=octetStringIntoShortArray(Octets);
    }
    
    /**	Copy an existing IP Address into a new object.
     * @param master
     */
    public IpAddress(IpAddress master){
	short[] aMaster=master.getOctetArray();
	aOctets=new short[aMaster.length];
	for (int i=0; i < aMaster.length; i++) {
	    aOctets[i]=aMaster[i];
	}
    }
    private IpAddress(short[] octets){		aOctets=octets;	}
    /**	Apply a netmask to an address, leaving only the network portion of the
     *	address.<BR><BR>
     *
     *	I.E. 208.139.197.195 netmask 255.255.255.224 yields 208.139.197.192
     *	@param Octets
     */
    public IpAddress applyNetMask(String Octets) throws MalformedIpAddressException {
	return applyNetMask(new IpAddress(Octets));
    }
    /**	Given the current address, get the broadcast address for the specified	
     *	netmask.	
     *	@param Mask
     */	
    public IpAddress getBroadcastAddress(String Mask) throws MalformedIpAddressException {
	IpAddress IPMask=new IpAddress(Mask);
	IpAddress IPResult;
	short[] aMask=IPMask.getOctetArray();
	short[] aResult=new short[aOctets.length];
	
	for (int i=0; i < aMask.length; i++) {
	    aResult[i]=aOctets[i];
	    aResult[i]&=aMask[i];	
	    aMask[i]=(byte)(~aMask[i] & 0xffff);
	    aResult[i]|=aMask[i];
	    if (aResult[i] < 0) {
		aResult[i]=(short)(256-((byte)(aResult[i])*-1));
	    }		
	}
	IPResult=new IpAddress(aResult);

	return IPResult;
    }
 
   
    /** Apply a netmask to an address, leaving only the network	
     *	portion of the address.	
     *	
     *	<CODE>	
     *	IpAddress ip=new IpAddress("192.168.0.23");	
     *	ip.applyNetMask("255.255.255.0");	
     *	System.out.println(ip.toString());	
     *	// returns "192.168.0.0"	
     *	</CODE>	
     *	@param mask
     */	
    public IpAddress applyNetMask(IpAddress mask){
	short[] aMask;
	aMask=mask.getOctetArray();
	for (int i=0; i < aOctets.length; i++){
	    aOctets[i]&=aMask[i];
	}
	return this;
    }
    
 
    public int hashCode(){
	// Boy, I'm not sure but let's try something like
	int iHash=0;
	for (int i=0; i < aOctets.length; i++) {
	    iHash=(iHash<<8)+aOctets[i];
	}
	return iHash;
    }
    
    
    /**
     * Check to see if this IpAddress is equal to Obj.
     * Handled types of obj are String, which is assumed
     * to be a dotted quad, or another IpAddress object.
     * @param obj The object to compare.
     */
    public boolean equals(Object obj) {
	boolean bResult=false;
	if (obj==null) {
	    // Nothing needed.		} else if (obj instanceof String) {
	    bResult=this.toString().equals(obj);
	} else if (obj instanceof IpAddress) {
	    short[] aCompare=((IpAddress)obj).getOctetArray();
	    bResult=(aCompare.length==aOctets.length);
	    for (int i=0; i < aOctets.length; i++) {
		bResult&=(aOctets[i]==aCompare[i]);
	    }
	} else {
	    // don't know how to handle this.
	}
	return bResult;
    }
 
   
    /**	
     * Return the IP Address as an array of Shorts.
     */
    public short[] getOctetArray(){
	short[] aCopy =new short[aOctets.length];
	System.arraycopy(aOctets,0,aCopy,0,aOctets.length);
	return aCopy;
    }

    
    /**	
     * Return the dotted notation of the IP Address as a string.
     */
    public String toString(){
	StringBuffer sb=new StringBuffer();
	for (int i=0; i < aOctets.length; i++) {
	    sb.append(aOctets[i]);
	    if (i < (aOctets.length-1)) {
		sb.append('.');
	    }
	}
	return sb.toString();
    }

    
    /**	
     * Helper method to convert a string representation into a short[] array.
     * @param Octets
     */
    private static short[] octetStringIntoShortArray(String Octets) throws MalformedIpAddressException {
	int iOctetCount=0, iOctet;
	short[] aResults;
	short[] aTemp=new short[MAX_IP_OCTETS];
	char[] aChars;
	
	Octets+=".";
	aChars=Octets.toCharArray();
	
	StringBuffer sbTemp=new StringBuffer();
	
	for (int i=0; i < aChars.length; i++) {
	    switch (aChars[i]) {
	    case '.' :
		try {
		    iOctet=Integer.parseInt(sbTemp.toString());
		} catch (NumberFormatException nfe) {
		    throw new MalformedIpAddressException();
		}
		
		if (iOctet < 0 || iOctet > 255) {
		    throw new MalformedIpAddressException();
		}
		
		aTemp[iOctetCount]=(short)iOctet;
		sbTemp.setLength(0);
		iOctetCount++;
		break;
	    default :
		sbTemp.append(aChars[i]);
		break;
	    }
	}
	if (iOctetCount==0) {
	    throw new MalformedIpAddressException();
	}
	
	aResults=new short[iOctetCount];
	for (int i=0; i < iOctetCount; i++) {
	    aResults[i]=aTemp[i];
	}
	
	return aResults;
    }
    
    /**
     *	This is a convenience method to check if a specified address resides in a
     *	specified network.
     *	@param address
     *	@param network
     *	@param netmask
     */
    public static boolean isAddressInNetwork(String address,String network, String netmask) {
	boolean bResult=false;
	
	if (address==null || network==null || netmask==null) {
	    return false;
	}
	
	try {
	    IpAddress ip=new IpAddress(address);
	    ip.applyNetMask(netmask);
	    bResult=ip.toString().equals(network);
	} catch (MalformedIpAddressException mfe) {
	    mfe.printStackTrace(System.err);
	}
	
	return bResult;
    }
    
    /*
      public static void main(String[] argv) throws MalformedIpAddressException {
      IpAddress ip=new IpAddress();
      System.out.println("toString="+ip.toString());
      
      ip=new IpAddress("208.139.197.195");
      System.out.println("toString="+ip.toString());
      
      ip.applyNetMask("255.255.255.224");
      System.out.println("Apply Netmask="+ip.toString());
      
      IpAddress ip2=new IpAddress(ip);
      
      System.out.println("ip.equals(IpAddress ip2) returned "+ip.equals(ip2));
      System.out.println("ip.equals(String Octets) returned "+ip.equals(ip2.toString()));
      
      System.out.println("ip.equals(111.111.111.111) returned "+ip.equals("111.111.111.111"));
      
      
      ip.applyNetMask("0.0.0.0");
      System.out.println("Apply Netmask="+ip.toString());
	}
    */
    
}
